package physics.collision;

/**
 * This wrapper class allows all the information regarding the direction of a
 * collision to be passed as a single object.
 * 
 * @author geo4
 * 
 */
public class CollisionEvent {

    /**
     * An enumeration of the directions in which a collision between 2D tiles
     * can occur.
     */
    public enum Direction {
        /**
         * Collision occurred from above.
         */
        TOP,
        /**
         * Collision occurred from below.
         */
        BOTTOM,
        /**
         * Collision occurred from the left.
         */
        LEFT,
        /**
         * Collision occurred from the right.
         */
        RIGHT,
        /**
         * Collision occurred from either side.
         */
        SIDE;
    }

    private static final String INVALID_DIRECTION_MESSAGE = "Not a valid direction.";

    private Collidable myCollidingObject;
    private Collidable myImpactedObject;

    private boolean myCollidedTop;
    private boolean myCollidedBottom;
    private boolean myCollidedLeft;
    private boolean myCollidedRight;

    /**
     * Makes a new CollisionEvent representing a collision in the direction
     * specified. Passing this method null means that no collision occurred.
     * 
     * @param collidingObject object that is actively colliding
     * @param impactedObject object that is being hit by the collidingObject
     * @param collisionDirection direction from the enumeration in which the
     *        collision occurred. LEFT or RIGHT must be specified (SIDE is not a
     *        valid parameter for the constructor).
     */
    public CollisionEvent (Collidable collidingObject, Collidable impactedObject,
                           Direction collisionDirection) {
        if (null == collisionDirection) {
            myCollidedTop = false;
            myCollidedBottom = false;
            myCollidedLeft = false;
            myCollidedRight = false;
        }
        else {
            myImpactedObject = impactedObject;

            switch (collisionDirection) {
                case TOP:
                    myCollidedTop = true;
                    break;
                case BOTTOM:
                    myCollidedBottom = true;
                    break;
                case LEFT:
                    myCollidedLeft = true;
                    break;
                case RIGHT:
                    myCollidedRight = true;
                    break;
                default:
                    throw new RuntimeException(INVALID_DIRECTION_MESSAGE);
            }
        }
    }

    /**
     * Checks if a collision occurred in any direction.
     * 
     * @return true if a collision in any direction occurred
     */
    public boolean collided () {
        return myCollidedTop || myCollidedBottom || myCollidedLeft || myCollidedRight;
    }

    /**
     * Checks if a collision occurred in a particular direction.
     * 
     * @param direction direction of interest specified by the enumeration
     * @return true if a collision occurred in the specified direction
     */
    public boolean collided (Direction direction) {
        switch (direction) {
            case TOP:
                return myCollidedTop;
            case BOTTOM:
                return myCollidedBottom;
            case LEFT:
                return myCollidedLeft;
            case RIGHT:
                return myCollidedRight;
            case SIDE:
                return myCollidedLeft || myCollidedRight;
            default:
                throw new RuntimeException(INVALID_DIRECTION_MESSAGE);

        }
    }

    /**
     * Returns the opposing and opposite CollisionEvent.
     * 
     * @return the CollisionEvent corresponding to the object that was collided
     *         with.
     */
    public CollisionEvent invertCollision () {
        CollisionEvent c;
        if (myCollidedTop) {
            c = new CollisionEvent(myImpactedObject, myCollidingObject, Direction.BOTTOM);
        }
        else if (myCollidedBottom) {
            c = new CollisionEvent(myImpactedObject, myCollidingObject, Direction.TOP);
        }
        else if (myCollidedLeft) {
            c = new CollisionEvent(myImpactedObject, myCollidingObject, Direction.RIGHT);
        }
        else if (myCollidedRight) {
            c = new CollisionEvent(myImpactedObject, myCollidingObject, Direction.LEFT);
        }
        else {
            c = new CollisionEvent(myImpactedObject, myCollidingObject, null);
        }
        return c;
    }

    /**
     * Returns the object that hit the impacted object.
     * 
     * @return the actively colliding object
     */
    public Collidable getCollidingObject () {
        return myCollidingObject;
    }

    /**
     * Returns the object that is being hit by the colliding object.
     * 
     * @return the passively hit object
     */
    public Collidable getImpactedObject () {
        return myImpactedObject;
    }
}
